package main

import (
	"bufio"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	_ "text/template"
	"html/template"
	"time"
	"unicode"
	"unicode/utf8"
)

func test1() {
	var a [3]int
	fmt.Println(a[0])
	fmt.Println(a[len(a) - 1])

	for i, v := range a{
		fmt.Printf("%d %d\n", i, v)
	}

	q:=[...]int{1, 2, 3}
	fmt.Printf("%T\n", q)
}


func test2() {
	type Currency int
	const  (
		USD Currency = iota
		EUR
		GBP
		RMB
	)
	symbol := [...]string{USD: "$", EUR: "€", GBP: "￡", RMB: "￥"}
	fmt.Println(RMB, symbol[RMB])
}

func test3() {
	a :=[2]int{1, 2}
	b:= [...]int{1, 2}
	c := [2]int{1, 3}
	fmt.Println(a == b, a ==c, b == c)
}

func test4() {
	c1 := sha256.Sum256([]byte("X"))
	c2 := sha256.Sum256([]byte("x"))
	fmt.Printf("%x\n%x\n%t\n%T\n", c1, c2, c1 == c2, c1)
}

func exec1(s1, s2 string) int {
	count :=0;
	c1 := sha256.Sum256([]byte(s1))
	c2 := sha256.Sum256([]byte(s2))
	fmt.Printf("%x\n%x\n", c1, c2)
	for i := 0; i < 32; i++ {
		for ii := 0; ii < 8; ii++ {
			if c1[i]>>ii != c2[i] >> ii{
				count++
			}
		}
	}

	return count
}

func test5() {
	s1 := "X"
	s2 := "X"
	fmt.Println(exec1(s1, s2))
}

var cliCmd = flag.String("sha", "sha256", "input your select sha256/sha384")

func exec2()  {
	flag.Parse()
	reader := bufio.NewReader(os.Stdin)
	//str, err := reader.ReadString('\n')
	str , _, err := reader.ReadLine()
	if err != nil{
		return
	}
	if strings.Compare(*cliCmd, "sha256") == 0 {
		fmt.Printf("%x\n", sha256.Sum256([]byte(str)))
	}else if strings.Compare(*cliCmd, "sha384") == 0 {
		w := sha512.New384()
		fmt.Printf("%x\n", w.Sum([]byte(str)))
	}
}

func reverse(s []int) {
	for i, j := 0, len(s) -1; i < j; i, j = i + 1, j - 1{
		s[i], s[j] = s[j], s[i]
	}
}

func test6() {
	a := [...]int{0, 1, 2, 3, 4, 5, 6}
	fmt.Println(a)
	reverse(a[:])
	fmt.Println(a)

}

func noneempty(strings []string) []string {
	i := 0
	for _, s := range strings{
		if s!="" {
			strings[i] = s
			i++
		}
	}
	return strings[:i]
}

func test7() {
	data := []string{"one", "", "three"}
	fmt.Printf("%q\n", noneempty(data))
	fmt.Printf("%q\n", data)
}

func test8() {
	seen := make(map[string]bool)
	input := bufio.NewScanner(os.Stdin)
	for input.Scan(){
		line := input.Text()
		if !seen[line]{
			seen[line] = true
			fmt.Println(line)
		}
	}

	if err := input.Err();err !=nil{
		fmt.Fprintf(os.Stderr, "dedup: %v\n", err)
		os.Exit(1)
	}
}

func charCount() {
	counts := make(map[rune]int)
	var utflen[utf8.UTFMax+1]int
	invalid := 0

	in := bufio.NewReader(os.Stdin)
	for  {
		r, n, err := in.ReadRune()
		if err == io.EOF{
			break
		}
		if err != nil {
			fmt.Fprintf(os.Stderr, "charcount: %v\n", err)
			os.Exit(1)
		}
		if r == unicode.ReplacementChar && n == 1 {
			invalid++
			continue
		}
		counts[r]++
		utflen[n]++
	}

	fmt.Printf("rune\tcount\n")
	for c, n := range counts {
		fmt.Printf("%q\t%d\n", c, n)
	}
	fmt.Printf("\nlen\tcount\n")
	for i, n := range utflen {
		if i > 0 {
			fmt.Printf("%d\t%d\n", i, n)
		}
	}
	if invalid > 0 {
		fmt.Printf("\n%d invalid UTF-8 characters\n", invalid)
	}
}

type tree struct {
	value int
	left, right *tree
}

func Sort(values []int) {
	var root *tree
	for _, v := range values{
		root = add(root, v)
	}
	appendValues(values[:0], root)
}

func appendValues(values []int, t *tree) []int {
	if t != nil{
		values = appendValues(values, t.left)
		values = append(values, t.value)
		values = appendValues(values, t.right)
	}

	return values
}

func add(t *tree, value int) *tree {
	if t == nil{
		t = new(tree)
		t.value = value
		return t
	}
	if value < t.value {
		t.left = add(t.left, value)
	}else {
		t.right = add(t.right, value)
	}
	return t
}

type Point struct {
	X, Y int
}

type Circle struct {
	Point
	Radius int
}

type Wheel struct {
	Circle
	Spokes int
}

func test9() {
	var w = Wheel{Circle{Point{8, 8}, 5}, 20}
	fmt.Printf("%v\n", w)
	fmt.Printf("%#v\n", w)
}

type Movie struct {
	Title string
	Year int `json:"released"`
	Color bool `json:"color,omitempty"`
	Actors []string
}

var movies = []Movie{
	{Title: "Casablanca", Year: 1942, Color: false,
		Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
	{Title: "Cool Hand Luke", Year: 1967, Color: true,
		Actors: []string{"Paul Newman"}},
	{Title: "Bullitt", Year: 1968, Color: true,
		Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
}

func test10() {
	//data, err := json.Marshal(movies)
	data, err := json.MarshalIndent(movies, "", "    ")
	if err != nil {
		log.Fatalf("JSON marshaling failed: %s", err)
	}
	fmt.Printf("%s\n", data)
}

const IssuesURL = "https://api.github.com/search/issues"

type IssuesSearchResult struct {
	TotalCount int `json:"total_count"`
	Items []*Issue
}

type Issue struct {
	Number int
	HTMLURL string `json:"html_url"`
	Title string
	State string
	User  *User
	CreatedAt time.Time `json:"created_at"`
	Body string
}

type User struct {
	Login string
	HTMLURL string `json:"html_url"`
}

func SearchIssues(terms []string) (*IssuesSearchResult, error) {
	q := url.QueryEscape(strings.Join(terms, " "))
	resp, err := http.Get(IssuesURL + "?q=" + q)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("search query failed: %s", resp.Status)
	}

	var result IssuesSearchResult
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil{
		resp.Body.Close()
		return nil, err
	}

	resp.Body.Close()
	return &result, nil
}

func test11() {
	time.Now()
	result, err := SearchIssues(os.Args[1:])
	if err != nil{
		log.Fatal(err)
	}

	fmt.Printf("%d issues: \n", result.TotalCount)
	for _, item := range result.Items{
		fmt.Printf("#%-5d %9.9s %.55s\n",
			item.Number, item.User.Login, item.Title)
	}
}

func test12() {
	now := time.Now()
	yearAgo := now.AddDate(-1, 0, 0)
	monthAgo := now.AddDate(0, -1, 0)

	yearAgos := make([]* Issue, 0)
	monthAgos := make([]* Issue, 0)
	lessMonths := make([]* Issue, 0)

	result, err := SearchIssues(os.Args[1:])
	if err != nil{
		log.Fatal(err)
	}
	fmt.Printf("%d issues:\n", result.TotalCount)
	for _, item := range result.Items{
		if yearAgo.After(item.CreatedAt){
			yearAgos = append(yearAgos, item)
		}else if monthAgo.After(item.CreatedAt){
			monthAgos = append(monthAgos, item)
		}else if monthAgo.Before(item.CreatedAt) {
			lessMonths = append(lessMonths, item)
		}
	}

	fmt.Printf("\n一年前\n")
	for _, item := range yearAgos {
		fmt.Printf("#%-5d %9.9s %.55s %v\n",
			item.Number, item.User.Login, item.Title, item.CreatedAt)
	}

	fmt.Printf("\n一月前\n")
	for _, item := range monthAgos {
		fmt.Printf("#%-5d %9.9s %.55s %v\n",
			item.Number, item.User.Login, item.Title, item.CreatedAt)
	}

	fmt.Printf("\n不足一月\n")
	for _, item := range lessMonths {
		fmt.Printf("#%-5d %9.9s %.55s %-40v\n",
			item.Number, item.User.Login, item.Title, item.CreatedAt)
	}
}

const temp1 = `{{.TotalCount}} issues:
{{range .Items}}----------------------------------------
Number:		{{.Number}}
User:		{{.User.Login}}
Title:		{{.Title | printf "%.64s"}}
Age:		{{.CreatedAt | daysAgo}} days
{{end}}`

func daysAgo(t time.Time) int {
	return int(time.Since(t).Hours() / 24)
}

var report = template.Must(template.New("issuelist").
		Funcs(template.FuncMap{"daysAgo":daysAgo}).
		Parse(temp1))

var issueList = template.Must(template.New("issuelist").
		Parse(`
<h1>{{.TotalCount}} issues </h1>
<table>
<tr style='text-align: left'>
<th>#</th>
<th>State</th>
<th>User</th>
<th>Title</th>
</tr>
{{range .Items}}
<tr>
<td><a href='{{.HTMLURL}}'>{{.Number}}</a></td>
<td>{{.State}}</td>
<td><a href='{{.User.HTMLURL}}'>{{.User.Login}}</a></td>
<td><a href='{{.HTMLURL}}'>{{.Title}}</a></td>
</tr>
{{end}}
</table>
`))
func test13() {
	result, err := SearchIssues(os.Args[1:])
	if err != nil{
		log.Fatal(err)
	}
	//if err := report.Execute(os.Stdout, result); err != nil{
	if err := issueList.Execute(os.Stdout, result); err != nil{
		log.Fatal(err)
	}
}
/*
func test13() {
	report, err := template.New("report").
		Funcs(template.FuncMap{"daysAgo": daysAgo}).
		Parse(temp1)
	if err != nil {
		log.Fatal(err)
	}
}
*/

func test14() {
	const temp2 = `<p>A: {{.A}}</p><p>B: {{.B}}</p>`
	t := template.Must(template.New("escape").Parse(temp2))
	var data struct{
		A string
		B template.HTML
	}

	data.A = "<b>Hello!</b>"
	data.B = "<b>Hello!</b>"
	if err := t.Execute(os.Stdout, data); err != nil{
		log.Fatal(err)
	}
}


func main() {
	//test1()
	//test2()
	//test3()
	//test4()
	//test5()
	//exec2()
	//test6()
	//test7()
	//test8()
	//charCount()
	//test9()
	//test10()
	//test11()
	//test12()
	//test13()
	test14()
}
